package com.longlonggo.config;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import com.longlonggo.shiro.realm.UserRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.util.ResourceUtils;

/**
 * 权限控制(shiro)配置类
 * 
 * @author 石马人山 me@longlonggo.com
 * @Created 2018年4月25日 下午5:33:31
 *          </p>
 * @version 100-000-000<br/>
 * @description <br/>
 */
@Configuration
public class ShiroConfig {
	Logger LOG  =  Logger.getLogger(ShiroConfig.class);

	/**
	 * shiro核心拦截器配置  <br/>
	 * 
	 * @param securityManager
	 *            该对象为apache包的，不是lang包的对象
	 * @return
	 */
	@Bean
	public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
		// 初始化shiro过滤器bean工厂
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		// 必须设置 SecurityManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// 设置要跳转到登录页地址,不设置则默认会自动寻找Web工程根目录下的"/login.jsp"页面
		shiroFilterFactoryBean.setLoginUrl("/login");
		// 登录成功后要跳转的链接，逻辑也可以自定义，例如返回上次请求的页面
		shiroFilterFactoryBean.setSuccessUrl("/index");
		// 未授权界面;
		shiroFilterFactoryBean.setUnauthorizedUrl("/error");

		// 拦截器配置
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		// 动态配置static目录下所有文件不被拦截
		filterChainDefinitionMap.put("/static/**", "anon");

		System.out.println("-----------------------------------ShiroConfig---Shiro拦截器开始注入");
		//获取static目录下所有目录名称，并将其加入到非拦截规则中
		File file = null;
		try {
			file = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "static");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

//		File file = new File("/src/main/resources/static");
        if(file.isDirectory()) {  
            File[] files = file.listFiles();  
            for (File f : files) {  
                if(f.isDirectory()){  
            		filterChainDefinitionMap.put("/" + f.getName() + "/**", "anon");
					System.out.println("/" + f.getName() + "/**"+ "anon");
                }else {
                	filterChainDefinitionMap.put("/" + f.getName(), "anon");
					System.out.println("/" + f.getName()+ "anon");
                }
            }  
        }  
        
		filterChainDefinitionMap.put("/login", "anon");
		// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
		filterChainDefinitionMap.put("/logout", "logout");
		// 过滤链定义，从上向下顺序执行，一般将 /**放在最为下边
		// authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
		filterChainDefinitionMap.put("/**", "authc");

		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		System.out.println("-----------------------------------ShiroConfig---Shiro拦截器工厂类注入成功");
		return shiroFilterFactoryBean;
	}
	
	
	/**
	 * 配置SHIRO核心安全事务管理器 
	 * 不指定名字的话，自动创建一个方法名第一个字母小写的bean
	 * 
	 * @Bean(name = "securityManager")
	 * @return
	 */
	@Bean
	public SecurityManager securityManager() {
		System.out.println("注入Shiro的Web过滤器-->securityManager:" + ShiroFilterFactoryBean.class);
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(userRealm());
		return securityManager;
	}

	/**
	 * 用于指定验证框架，使用自己自定义的验证方式
	 * @return
	 */
	@Bean
	public UserRealm userRealm() {
		UserRealm userRealm = new UserRealm();
		
		// 告诉realm,使用credentialsMatcher加密算法类来验证密文
		userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		userRealm.setCachingEnabled(false);
		return userRealm;
	}

	/**
	 * 凭证匹配器 ,用于密码比对（由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了（在UserRealm类中定义）
	 * 所以我们需要修改下doGetAuthenticationInfo中的代码; 
	 * 可以扩展凭证匹配器，实现 输入密码错误次数后锁定等功能
	 * 
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashIterations(2);// 散列的次数，比如散列两次，相当于 md5(md5(""));
		// storedCredentialsHexEncoded默认是true，此时用的是密码加密用的是Hex编码；false时用Base64编码
		hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
		return hashedCredentialsMatcher;
	}
	
//	//配置缓存管理  
//    @Bean(name="cacheManager")  
//    public EhCacheManager cacheManager(){  
//        EhCacheManager cacheManager = new EhCacheManager();  
//        cacheManager.setCacheManagerConfigFile("classpath:xml/spring-shiro-ehcache.xml");  
//        return cacheManager;  
//    }  

	/**
	 * Shiro生命周期处理器
	 * 
	 * @return
	 */
	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	/**
	 * 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
	 * DefaultAdvisorAutoProxyCreator，Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理
	 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
	 * 
	 * DependsOn注解 用来控制bean加载顺序https://blog.csdn.net/neweastsun/article/details/78775371
	 * @return
	 */
	@Bean
	@DependsOn({ "lifecycleBeanPostProcessor" })
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}

	/**
	 * 开启shiro aop注解支持(如@RequiresRoles,@RequiresPermissions)
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
		return authorizationAttributeSourceAdvisor;
	}
	
	public static void main(String[] args) {
        String hashAlgorithmName = "MD5";
        String credentials = "123456";
        int hashIterations = 2;
        Object obj = new SimpleHash(hashAlgorithmName, credentials, null, hashIterations);
        System.out.println(obj);
    }
}
